#define UNICODE
#include <leaf.h>
#include <GdiPlus.h>

extern "C"
{
	HWND CreateAlphaWindow();
	void AlphaFillRect(RECT *r, long c);
	void AlphaTextOut(int x, int y, UINT flags, RECT *r, char *str, UINT len, int *padding);
	void SetAlphaAreaPos(int x, int y, int width, int height, UINT flags);
	HWND textwin;
	HWND mainwin;
	HDC textdc;
	extern HDC s_hdc;
	extern LRESULT CALLBACK _TextAreaWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	extern LRESULT CALLBACK _WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	extern void PaintMemoryDC(HWND hwnd, PAINTSTRUCT *ps);
}

using namespace Leaf::UI;
using namespace Leaf::App;

long ClientPaint(HWND h, UINT msg, WPARAM wp, LPARAM lp);


class AlphaArea : public Window
{
public:
	int alpha;
	int cleft, ctop, cwidth, cheight;
	int width, height;
	MemoryDC *memdc;
	Bitmap *membmp;

	AlphaArea();
	int CreateDefault(Window *parent = NULL);
	long OnPaint(const MessageArgs *ma);
	long OnSize(const MessageArgs *ma);
	void ResetMemoryDC();
	void PrepareMemoryDC();
	void TransparentPaint();
	void ClientFillRect(RECT *rect, long color);
	void OutText(int x, int y, UINT flags, RECT *r, char *str, UINT len, int *padding);
	//void FillRect(RECT *r, HBRUSH b);
	virtual LRESULT CALLBACK MessageProc(UINT msg, WPARAM wp, LPARAM lp);
};

AlphaArea client;

class VimWindow : public Window
{
public:
	Brush transbg;
	COLORREF bgcolor;

	VimWindow();
	int CreateDefault(Window *parent = NULL);
	long OnPaint(const MessageArgs *ma);
	long OnMove(const MessageArgs *ma);
	long OnSize(const MessageArgs *ma);
	virtual LRESULT CALLBACK MessageProc(UINT msg, WPARAM wp, LPARAM lp);
	void SetAlphaAreaPos(int x, int y, int width, int height, UINT flags);
};

AlphaArea::AlphaArea()
{
	alpha = 180;
	memdc = NULL;
	membmp = NULL;
	cleft = ctop = 0;
	cwidth = cheight = 0;
	width = height = 0;
}

int AlphaArea::CreateDefault(Window *parent)
{
	if(! Window::CreateDefault(L"", parent))
		return 0;

	SetWindowLong(h, GWL_EXSTYLE,
			GetWindowLong(h, GWL_EXSTYLE) | WS_EX_LAYERED);

	//SetWindowLong(h, GWL_STYLE,
	//		WS_VISIBLE | WS_POPUP);

	//OnMessage(WM_PAINT, this, (Leaf::UI::MemberHandler)&AlphaArea::OnPaint);
	OnMessage(WM_SIZE, this, (Leaf::UI::MemberHandler)&AlphaArea::OnSize);
	return 1;
}

LRESULT AlphaArea::MessageProc(UINT msg, WPARAM wp, LPARAM lp)
{
	MessageArgs ma;
	PAINTSTRUCT ps;

	ma.control = this;
	ma.message = msg;
	ma.lparam = lp;
	ma.wparam = wp;

	if(msg == WM_LBUTTONDOWN)
		return OnPaint(&ma);
	else if(msg == WM_PAINT)	// avoid WM_PAINT not removed, into dead loop in C side
	{
		::BeginPaint(h, &ps);
		::EndPaint(h, &ps);
		return 0;//RELAY;
	}
	else
		return _TextAreaWndProc(h, msg, wp, lp);
}

void AlphaArea::ClientFillRect(RECT *prect, long color)
{
	int *p;
	int ymin, ymax;
	int xmin, xmax;

	if(!prect)
		return;

	if(!membmp || !memdc)
		return;

	if(!membmp->data)
		return;

	p = (int *)(membmp->data);

	//for(int y = height - prect->top - ctop; y > height - prect->bottom - ctop; y --)
	//	for(int x = prect->left + cleft; x < prect->right + cleft; x ++)
	ymax = height - prect->top - 1; if(ymax > height - 1) ymax = height - 1;
	ymin = height - prect->bottom; if(ymin < 0)	ymin = 0;
	xmax = prect->right; if(xmax > width) xmax = width;
	xmin = prect->left;  if(xmin < 0) xmin = 0;

	for(int y = ymax; y > ymin; y --)
		for(int x = xmax; x < xmin; x ++)
			p[x + y * width] = color;
}

void AlphaArea::OutText(int x, int y, UINT flags, RECT *r, char *str, UINT len, int *padding)
{
	int *p;
	int *pixel;
	RECT rect;

	if(!memdc)
		return;

	if(!membmp)
		return;

	if(!r)
	{
		GetClientRect(h, &rect);
		r = &rect;
	}

	if(membmp->data)
	{
		p = (int *)(membmp->data);
		ExtTextOutA(memdc->h, x, y, flags, r, str, len, padding);
		for(int y = height - r->top - 1; y > height - r->bottom; y --)
			for(int x = r->left; x < r->right; x ++)
			{
				pixel =	&(p[x + y * width]);
				if(!(*pixel && 0xff000000))
					*pixel |= 0xff000000;
			}
	}
}

long AlphaArea::OnSize(const MessageArgs *ma)
{
	RECT rect;

	::GetWindowRect(h, &rect);
	width = rect.right - rect.left;
	height = rect.bottom - rect.top;
	ResetMemoryDC();
	PrepareMemoryDC();
	return RELAY;
}

long AlphaArea::OnPaint(const MessageArgs *ma)
{
	RECT frect;
	PAINTSTRUCT ps;

	//BeginPaint(h, &ps);
	PrepareMemoryDC();

	LineTo(memdc->h, 100, 200);
	s_hdc = memdc->h;
	PaintMemoryDC(h, &ps);
	//::EndPaint(h, &ps);

	TransparentPaint();
	return 0;
}

void AlphaArea::PrepareMemoryDC()
{
	DC dc;
	RECT rect;

	if(memdc)
		return;

	memdc = new MemoryDC();
	membmp = new Bitmap();

	if(!dc.SetClientDC(this))
		return;

	::GetClientRect(h, &rect);
	width = rect.right - rect.left;
	height = rect.bottom - rect.top;

	membmp->CreateDefaultDIB(&dc, width, height);
	memdc->CreateCompatible(&dc);
	memdc->SetBitmap(membmp);	
	s_hdc = memdc->h;

	ClientFillRect(&rect, 0x77ff0000);

	return;
}

void AlphaArea::ResetMemoryDC()
{
	if(memdc)
	{
		delete memdc;
		delete membmp;
		memdc = NULL;
		membmp = NULL;
	}
}

void AlphaArea::TransparentPaint()
{
	BLENDFUNCTION blend;
	POINT pt;
	SIZE  sz;

	if(!memdc)
		PrepareMemoryDC();

	blend.AlphaFormat = AC_SRC_ALPHA;
	blend.BlendOp = AC_SRC_OVER;
	blend.BlendFlags = 0;
	blend.SourceConstantAlpha = 255;

	pt.x = pt.y = 0;
	sz.cx = width;
	sz.cy = height;

	::UpdateLayeredWindow(h, NULL, NULL, &sz,
				memdc->h, &pt, RGB(255, 255, 0), &blend, ULW_ALPHA | ULW_OPAQUE);

	return;
}
/*
void AlphaArea::FillRect(RECT *r, HBRUSH b)
{
	RECT rect;

	if(r && b)
	{
		rect = *r;
		rect.left += cleft;
		rect.right += cleft;
		rect.top += ctop;
		rect.bottom += ctop;
		::FillRect(memdc->h, &rect, b);
		OnPaint(NULL);
	}

}*/










AlphaArea ap;
VimWindow win;


VimWindow::VimWindow()
{
	bgcolor = RGB(121, 122, 123);
}

int VimWindow::CreateDefault(Window *parent)
{
	if(! Window::CreateDefault(L"", NULL))
		return 0;

	client.CreateDefault(this);
	SetWindowLong(h, GWL_EXSTYLE,
			GetWindowLong(h, GWL_EXSTYLE) | WS_EX_LAYERED);

	if(this != &win)
		return 0;

	//OnMessage(WM_PAINT, this, (Leaf::UI::MemberHandler)&VimWindow::OnPaint);
	//OnMessage(WM_MOVE, &win, (Leaf::UI::MemberHandler)&VimWindow::OnMove);
	//OnMessage(WM_SIZE, &win, (Leaf::UI::MemberHandler)&VimWindow::OnSize);

	SetLayeredWindowAttributes(h, bgcolor, 0, LWA_COLORKEY);
	
	return 1;
}

void VimWindow::SetAlphaAreaPos(int x, int y, int width, int height, UINT flags)
{
	POINT lefttop;

	lefttop.x = x;
	lefttop.y = y;

	::ClientToScreen(h, &lefttop);

	::SetWindowPos(client.h, NULL, lefttop.x, lefttop.y, width, height, flags);
}

LRESULT VimWindow::MessageProc(UINT msg, WPARAM wp, LPARAM lp)
{
	return _WndProc(h, msg, wp, lp);
}

long VimWindow::OnPaint(const MessageArgs *ma)
{
	DC dc;
	RECT rect;
	Brush b;

	if(dc.SetPaintDC(this))
		if(b.CreateDefault(bgcolor))
		{
			::GetClientRect(h, &rect);
			::FillRect(dc.h, &rect, b.h);
		}

	return INTERCEPT;
}

long VimWindow::OnMove(const MessageArgs *ma)
{
	int cwidth, cheight;
	int cleft, ctop;
	RECT crect;
	POINT lefttop, rightbot;

	int r = ::GetClientRect(this->h, &crect);
	DWORD err = ::GetLastError();
	cwidth = crect.right - crect.left;
	cheight = crect.bottom - crect.top;

	lefttop.x = lefttop.y = 0;
	rightbot.x = cwidth;
	rightbot.y = cheight;
	::ClientToScreen(h, &lefttop);
	::ClientToScreen(h, &rightbot);
	cleft = lefttop.x;
	ctop = lefttop.y;

	::MoveWindow(client.h, cleft, ctop, cwidth, cheight, true);
	return 0;
}

long VimWindow::OnSize(const MessageArgs *ma)
{
	OnMove(ma);
	return 0;
}










HWND CreateAlphaWindow()
{
//	ULONG_PTR gdixtok;
//	Gdiplus::GdiplusStartupInput gdixinput;

//	Gdiplus::GdiplusStartup(&gdixtok, &gdixinput, NULL);
	WindowBase::Init();
	win.CreateDefault();//L"Hello");
	//ap.CreateDefault();
	textwin = (HWND)(client.h);
	//mainwin = (HWND)win.h;
	//textdc = win.client.memdc->h;
	return win.h;
}

void AlphaFillRect(RECT *r, long c)
{
	if(!(c & 0xff000000))
		c |= 0x99000000;
	client.ClientFillRect(r, c);
}

void AlphaTextOut(int x, int y, UINT flags, RECT *r, char *str, UINT len, int *padding)
{
	client.OutText(x, y, flags, r, str, len, padding);
}

void SetAlphaAreaPos(int x, int y, int width, int height, UINT flags)
{
	win.SetAlphaAreaPos(x, y, width, height, flags);
}